package org.aerie.forest.extensions.base.brick.math;

import java.math.BigDecimal;
import java.math.BigInteger;

/**
 * @description 分数
 *
 * @author quark
 * @organization aerie
 * @date 2020年4月7日 下午10:12:18
 * @version 1.2.0
 */
public final class Fraction {

	/**
	 * @description 符号[默认是正号+(true)]
	 * 
	 * @author quark
	 * @date 2020年4月9日 下午1:36:12
	 * @version 1.2.0
	 */
	private boolean sign = true;

	/**
	 * @description 分子 
	 * 
	 * @author quark
	 * @date 2020年4月9日 下午1:39:19
	 * @version 1.2.0
	 */
	private BigInteger numerator;

	/**
	 * @description 分母
	 * 
	 * @author quark
	 * @date 2020年4月9日 下午1:39:28
	 * @version 1.2.0
	 */
	private BigInteger denominator;

	/**
	 * Construction
	 * 
	 * @param numerator   分子
	 * @param denominator 分母
	 *
	 * @date 2020年4月9日 下午1:40:02
	 * @version 1.2.0
	 */
	public Fraction(BigInteger numerator, BigInteger denominator) {
		super();
		int denominatorCompareZero = denominator.compareTo(BigInteger.ZERO);
		int numeratorCompareZero = numerator.compareTo(BigInteger.ZERO);
		if (denominatorCompareZero == 0)
			throw new ArithmeticException("分数的分母不能为零");
		// 优化
		if (numeratorCompareZero == 0) {
			this.numerator = numerator;
			this.denominator = BigInteger.ONE;
		}
		// 计算符号
		if (denominatorCompareZero > 0 ^ numeratorCompareZero > 0) {
			sign = false;
		}
		// 通过最大公约数优化
		BigInteger greatestCommonDivisor = greatestCommonDivisor(numerator.abs(), denominator.abs());
		this.numerator = numerator.divide(greatestCommonDivisor);
		this.denominator = denominator.divide(greatestCommonDivisor);

	}

	/**
	 * @description 获得分数的符号（true为正号）
	 * @return 符号标识
	 *
	 * @date 2020年4月11日 下午7:31:08
	 * @version 1.2.0
	 */
	public boolean isSign() {
		return sign;
	}

	/**
	 * @description 获得分子
	 * @return 分子
	 *
	 * @date 2020年4月11日 下午7:31:40
	 * @version 1.2.0
	 */
	public BigInteger getNumerator() {
		return numerator;
	}

	/**
	 * @description 获得分母
	 * @return 分母
	 *
	 * @date 2020年4月11日 下午7:31:52
	 * @version 1.2.0
	 */
	public BigInteger getDenominator() {
		return denominator;
	}

	/**
	 * @description 求最大公约数【内部方法，传参必须大于零】
	 * @param num1 第一个数
	 * @param num2 第二个数
	 * @return 最大公约数
	 *
	 * @date 2020年4月10日 上午11:25:07
	 * @version 1.2.0
	 */
	private BigInteger greatestCommonDivisor(BigInteger num1, BigInteger num2) {
		BigInteger mod = num1.mod(num2);
		if (mod.compareTo(BigInteger.ZERO) == 0)
			return num2;
		// 递归调用
		else
			return greatestCommonDivisor(num2, mod);
	}

	/**
	 * @description 转成BigDecimal
	 * @return 转成的值
	 *
	 * @date 2020年4月10日 上午11:31:31
	 * @version 1.2.0
	 */
	public BigDecimal toBigDecimal(int precision) {
		BigDecimal result = new BigDecimal(numerator).divide(new BigDecimal(denominator));
		if (!sign)
			return result.negate().setScale(precision, BigDecimal.ROUND_HALF_UP);
		return result.setScale(precision, BigDecimal.ROUND_HALF_UP);
	}

	/**
	 * @description 转成string
	 * @return 转成的结果
	 *
	 * @date 2020年4月13日 上午12:30:40
	 * @version 1.2.0
	 */
	@Override
	public String toString() {
		StringBuilder result = new StringBuilder("(分数)");
		switch (numerator.compareTo(denominator)) {
		case -1:
			return result.append(numerator).append("/").append(denominator).toString();
		case 0:
			return result.append("1").toString();
		case 1:
			BigInteger integer = numerator.divide(denominator);
			BigInteger numeratorOfMixedNumber = numerator.mod(denominator);
			return result.append(integer).append("[").append(numeratorOfMixedNumber).append("/").append(denominator)
					.append("]#{").append(numerator).append("/").append(denominator).append("}").toString();
		default:
			return "此分数不合法";
		}
	}

	/**
	 * @description 将小数转换成分数
	 * @param bigDecimal 需要转换的小数
	 * @return 转成的分数
	 *
	 * @date 2020年4月13日 上午1:03:06
	 * @version 1.2.0
	 */
	public static final Fraction convertIntoFraction(BigDecimal bigDecimal) {
		BigDecimal denominatorByConvert = BigDecimal.TEN.pow(bigDecimal.scale());
		BigInteger numeratorByConvert = bigDecimal.multiply(denominatorByConvert).toBigInteger();
		return new Fraction(numeratorByConvert, denominatorByConvert.toBigInteger());
	}
}
